import katex from "../katex.mjs";

/* eslint-disable */

/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */

/* vim: set ts=2 et sw=2 tw=80: */

/*************************************************************
 *
 *  KaTeX mhchem.js
 *
 *  This file implements a KaTeX version of mhchem version 3.3.0.
 *  It is adapted from MathJax/extensions/TeX/mhchem.js
 *  It differs from the MathJax version as follows:
 *    1. The interface is changed so that it can be called from KaTeX, not MathJax.
 *    2. \rlap and \llap are replaced with \mathrlap and \mathllap.
 *    3. Four lines of code are edited in order to use \raisebox instead of \raise.
 *    4. The reaction arrow code is simplified. All reaction arrows are rendered
 *       using KaTeX extensible arrows instead of building non-extensible arrows.
 *    5. \tripledash vertical alignment is slightly adjusted.
 *
 *    This code, as other KaTeX code, is released under the MIT license.
 *
 * /*************************************************************
 *
 *  MathJax/extensions/TeX/mhchem.js
 *
 *  Implements the \ce command for handling chemical formulas
 *  from the mhchem LaTeX package.
 *
 *  ---------------------------------------------------------------------
 *
 *  Copyright (c) 2011-2015 The MathJax Consortium
 *  Copyright (c) 2015-2018 Martin Hensel
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
//
// Coding Style
//   - use '' for identifiers that can by minified/uglified
//   - use "" for strings that need to stay untouched
// version: "3.3.0" for MathJax and KaTeX
// Add \ce, \pu, and \tripledash to the KaTeX macros.
katex.__defineMacro("\\ce", function (context) {
  return chemParse(context.consumeArgs(1)[0], "ce");
});

katex.__defineMacro("\\pu", function (context) {
  return chemParse(context.consumeArgs(1)[0], "pu");
}); //  Needed for \bond for the ~ forms
//  Raise by 2.56mu, not 2mu. We're raising a hyphen-minus, U+002D, not
//  a mathematical minus, U+2212. So we need that extra 0.56.

katex.__defineMacro(
  "\\tripledash",
  "{\\vphantom{-}\\raisebox{2.56mu}{$\\mkern2mu" +
    "\\tiny\\text{-}\\mkern1mu\\text{-}\\mkern1mu\\text{-}\\mkern2mu$}}"
);
//  This is the main function for handing the \ce and \pu commands.
//  It takes the argument to \ce or \pu and returns the corresponding TeX string.
//

var chemParse = function chemParse(tokens, stateMachine) {
  // Recreate the argument string from KaTeX's array of tokens.
  var str = "";
  var expectedLoc = tokens[tokens.length - 1].loc.start;

  for (var i = tokens.length - 1; i >= 0; i--) {
    if (tokens[i].loc.start > expectedLoc) {
      // context.consumeArgs has eaten a space.
      str += " ";
      expectedLoc = tokens[i].loc.start;
    }

    str += tokens[i].text;
    expectedLoc += tokens[i].text.length;
  }

  var tex = texify.go(mhchemParser.go(str, stateMachine));
  return tex;
}; //
// Core parser for mhchem syntax  (recursive)
//

/** @type {MhchemParser} */

var mhchemParser = {
  //
  // Parses mchem \ce syntax
  //
  // Call like
  //   go("H2O");
  //
  go: function go(input, stateMachine) {
    if (!input) {
      return [];
    }

    if (stateMachine === undefined) {
      stateMachine = "ce";
    }

    var state = "0"; //
    // String buffers for parsing:
    //
    // buffer.a == amount
    // buffer.o == element
    // buffer.b == left-side superscript
    // buffer.p == left-side subscript
    // buffer.q == right-side subscript
    // buffer.d == right-side superscript
    //
    // buffer.r == arrow
    // buffer.rdt == arrow, script above, type
    // buffer.rd == arrow, script above, content
    // buffer.rqt == arrow, script below, type
    // buffer.rq == arrow, script below, content
    //
    // buffer.text_
    // buffer.rm
    // etc.
    //
    // buffer.parenthesisLevel == int, starting at 0
    // buffer.sb == bool, space before
    // buffer.beginsWithBond == bool
    //
    // These letters are also used as state names.
    //
    // Other states:
    // 0 == begin of main part (arrow/operator unlikely)
    // 1 == next entity
    // 2 == next entity (arrow/operator unlikely)
    // 3 == next atom
    // c == macro
    //

    /** @type {Buffer} */

    var buffer = {};
    buffer["parenthesisLevel"] = 0;
    input = input.replace(/\n/g, " ");
    input = input.replace(/[\u2212\u2013\u2014\u2010]/g, "-");
    input = input.replace(/[\u2026]/g, "..."); //
    // Looks through mhchemParser.transitions, to execute a matching action
    // (recursive)
    //

    var lastInput;
    var watchdog = 10;
    /** @type {ParserOutput[]} */

    var output = [];

    while (true) {
      if (lastInput !== input) {
        watchdog = 10;
        lastInput = input;
      } else {
        watchdog--;
      } //
      // Find actions in transition table
      //

      var machine = mhchemParser.stateMachines[stateMachine];
      var t = machine.transitions[state] || machine.transitions["*"];

      iterateTransitions: for (var i = 0; i < t.length; i++) {
        var matches = mhchemParser.patterns.match_(t[i].pattern, input);

        if (matches) {
          //
          // Execute actions
          //
          var task = t[i].task;

          for (var iA = 0; iA < task.action_.length; iA++) {
            var o; //
            // Find and execute action
            //

            if (machine.actions[task.action_[iA].type_]) {
              o = machine.actions[task.action_[iA].type_](
                buffer,
                matches.match_,
                task.action_[iA].option
              );
            } else if (mhchemParser.actions[task.action_[iA].type_]) {
              o = mhchemParser.actions[task.action_[iA].type_](
                buffer,
                matches.match_,
                task.action_[iA].option
              );
            } else {
              throw [
                "MhchemBugA",
                "mhchem bug A. Please report. (" + task.action_[iA].type_ + ")",
              ]; // Trying to use non-existing action
            } //
            // Add output
            //

            mhchemParser.concatArray(output, o);
          } //
          // Set next state,
          // Shorten input,
          // Continue with next character
          //   (= apply only one transition per position)
          //

          state = task.nextState || state;

          if (input.length > 0) {
            if (!task.revisit) {
              input = matches.remainder;
            }

            if (!task.toContinue) {
              break iterateTransitions;
            }
          } else {
            return output;
          }
        }
      } //
      // Prevent infinite loop
      //

      if (watchdog <= 0) {
        throw ["MhchemBugU", "mhchem bug U. Please report."]; // Unexpected character
      }
    }
  },
  concatArray: function concatArray(a, b) {
    if (b) {
      if (Array.isArray(b)) {
        for (var iB = 0; iB < b.length; iB++) {
          a.push(b[iB]);
        }
      } else {
        a.push(b);
      }
    }
  },
  patterns: {
    //
    // Matching patterns
    // either regexps or function that return null or {match_:"a", remainder:"bc"}
    //
    patterns: {
      // property names must not look like integers ("2") for correct property traversal order, later on
      empty: /^$/,
      else: /^./,
      else2: /^./,
      space: /^\s/,
      "space A": /^\s(?=[A-Z\\$])/,
      space$: /^\s$/,
      "a-z": /^[a-z]/,
      x: /^x/,
      x$: /^x$/,
      i$: /^i$/,
      letters: /^(?:[a-zA-Z\u03B1-\u03C9\u0391-\u03A9?@]|(?:\\(?:alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega|Gamma|Delta|Theta|Lambda|Xi|Pi|Sigma|Upsilon|Phi|Psi|Omega)(?:\s+|\{\}|(?![a-zA-Z]))))+/,
      "\\greek": /^\\(?:alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega|Gamma|Delta|Theta|Lambda|Xi|Pi|Sigma|Upsilon|Phi|Psi|Omega)(?:\s+|\{\}|(?![a-zA-Z]))/,
      "one lowercase latin letter $": /^(?:([a-z])(?:$|[^a-zA-Z]))$/,
      "$one lowercase latin letter$ $": /^\$(?:([a-z])(?:$|[^a-zA-Z]))\$$/,
      "one lowercase greek letter $": /^(?:\$?[\u03B1-\u03C9]\$?|\$?\\(?:alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega)\s*\$?)(?:\s+|\{\}|(?![a-zA-Z]))$/,
      digits: /^[0-9]+/,
      "-9.,9": /^[+\-]?(?:[0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+))/,
      "-9.,9 no missing 0": /^[+\-]?[0-9]+(?:[.,][0-9]+)?/,
      "(-)(9.,9)(e)(99)": function e99(input) {
        var m = input.match(
          /^(\+\-|\+\/\-|\+|\-|\\pm\s?)?([0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+))?(\((?:[0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+))\))?(?:([eE]|\s*(\*|x|\\times|\u00D7)\s*10\^)([+\-]?[0-9]+|\{[+\-]?[0-9]+\}))?/
        );

        if (m && m[0]) {
          return {
            match_: m.splice(1),
            remainder: input.substr(m[0].length),
          };
        }

        return null;
      },
      "(-)(9)^(-9)": function _(input) {
        var m = input.match(
          /^(\+\-|\+\/\-|\+|\-|\\pm\s?)?([0-9]+(?:[,.][0-9]+)?|[0-9]*(?:\.[0-9]+)?)\^([+\-]?[0-9]+|\{[+\-]?[0-9]+\})/
        );

        if (m && m[0]) {
          return {
            match_: m.splice(1),
            remainder: input.substr(m[0].length),
          };
        }

        return null;
      },
      "state of aggregation $": function stateOfAggregation$(input) {
        // ... or crystal system
        var a = mhchemParser.patterns.findObserveGroups(
          input,
          "",
          /^\([a-z]{1,3}(?=[\),])/,
          ")",
          ""
        ); // (aq), (aq,$\infty$), (aq, sat)

        if (a && a.remainder.match(/^($|[\s,;\)\]\}])/)) {
          return a;
        } //  AND end of 'phrase'

        var m = input.match(/^(?:\((?:\\ca\s?)?\$[amothc]\$\))/); // OR crystal system ($o$) (\ca$c$)

        if (m) {
          return {
            match_: m[0],
            remainder: input.substr(m[0].length),
          };
        }

        return null;
      },
      "_{(state of aggregation)}$": /^_\{(\([a-z]{1,3}\))\}/,
      "{[(": /^(?:\\\{|\[|\()/,
      ")]}": /^(?:\)|\]|\\\})/,
      ", ": /^[,;]\s*/,
      ",": /^[,;]/,
      ".": /^[.]/,
      ". ": /^([.\u22C5\u00B7\u2022])\s*/,
      "...": /^\.\.\.(?=$|[^.])/,
      "* ": /^([*])\s*/,
      "^{(...)}": function _(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "^{",
          "",
          "",
          "}"
        );
      },
      "^($...$)": function $$(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "^",
          "$",
          "$",
          ""
        );
      },
      "^a": /^\^([0-9]+|[^\\_])/,
      "^\\x{}{}": function x(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "^",
          /^\\[a-zA-Z]+\{/,
          "}",
          "",
          "",
          "{",
          "}",
          "",
          true
        );
      },
      "^\\x{}": function x(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "^",
          /^\\[a-zA-Z]+\{/,
          "}",
          ""
        );
      },
      "^\\x": /^\^(\\[a-zA-Z]+)\s*/,
      "^(-1)": /^\^(-?\d+)/,
      "'": /^'/,
      "_{(...)}": function _(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "_{",
          "",
          "",
          "}"
        );
      },
      "_($...$)": function _$$(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "_",
          "$",
          "$",
          ""
        );
      },
      _9: /^_([+\-]?[0-9]+|[^\\])/,
      "_\\x{}{}": function _X(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "_",
          /^\\[a-zA-Z]+\{/,
          "}",
          "",
          "",
          "{",
          "}",
          "",
          true
        );
      },
      "_\\x{}": function _X(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "_",
          /^\\[a-zA-Z]+\{/,
          "}",
          ""
        );
      },
      "_\\x": /^_(\\[a-zA-Z]+)\s*/,
      "^_": /^(?:\^(?=_)|\_(?=\^)|[\^_]$)/,
      "{}": /^\{\}/,
      "{...}": function _(input) {
        return mhchemParser.patterns.findObserveGroups(input, "", "{", "}", "");
      },
      "{(...)}": function _(input) {
        return mhchemParser.patterns.findObserveGroups(input, "{", "", "", "}");
      },
      "$...$": function $$(input) {
        return mhchemParser.patterns.findObserveGroups(input, "", "$", "$", "");
      },
      "${(...)}$": function $$(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "${",
          "",
          "",
          "}$"
        );
      },
      "$(...)$": function $$(input) {
        return mhchemParser.patterns.findObserveGroups(input, "$", "", "", "$");
      },
      "=<>": /^[=<>]/,
      "#": /^[#\u2261]/,
      "+": /^\+/,
      "-$": /^-(?=[\s_},;\]/]|$|\([a-z]+\))/,
      // -space -, -; -] -/ -$ -state-of-aggregation
      "-9": /^-(?=[0-9])/,
      "- orbital overlap": /^-(?=(?:[spd]|sp)(?:$|[\s,;\)\]\}]))/,
      "-": /^-/,
      "pm-operator": /^(?:\\pm|\$\\pm\$|\+-|\+\/-)/,
      operator: /^(?:\+|(?:[\-=<>]|<<|>>|\\approx|\$\\approx\$)(?=\s|$|-?[0-9]))/,
      arrowUpDown: /^(?:v|\(v\)|\^|\(\^\))(?=$|[\s,;\)\]\}])/,
      "\\bond{(...)}": function bond(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "\\bond{",
          "",
          "",
          "}"
        );
      },
      "->": /^(?:<->|<-->|->|<-|<=>>|<<=>|<=>|[\u2192\u27F6\u21CC])/,
      CMT: /^[CMT](?=\[)/,
      "[(...)]": function _(input) {
        return mhchemParser.patterns.findObserveGroups(input, "[", "", "", "]");
      },
      "1st-level escape": /^(&|\\\\|\\hline)\s*/,
      "\\,": /^(?:\\[,\ ;:])/,
      // \\x - but output no space before
      "\\x{}{}": function x(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "",
          /^\\[a-zA-Z]+\{/,
          "}",
          "",
          "",
          "{",
          "}",
          "",
          true
        );
      },
      "\\x{}": function x(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "",
          /^\\[a-zA-Z]+\{/,
          "}",
          ""
        );
      },
      "\\ca": /^\\ca(?:\s+|(?![a-zA-Z]))/,
      "\\x": /^(?:\\[a-zA-Z]+\s*|\\[_&{}%])/,
      orbital: /^(?:[0-9]{1,2}[spdfgh]|[0-9]{0,2}sp)(?=$|[^a-zA-Z])/,
      // only those with numbers in front, because the others will be formatted correctly anyway
      others: /^[\/~|]/,
      "\\frac{(...)}": function frac(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "\\frac{",
          "",
          "",
          "}",
          "{",
          "",
          "",
          "}"
        );
      },
      "\\overset{(...)}": function overset(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "\\overset{",
          "",
          "",
          "}",
          "{",
          "",
          "",
          "}"
        );
      },
      "\\underset{(...)}": function underset(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "\\underset{",
          "",
          "",
          "}",
          "{",
          "",
          "",
          "}"
        );
      },
      "\\underbrace{(...)}": function underbrace(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "\\underbrace{",
          "",
          "",
          "}_",
          "{",
          "",
          "",
          "}"
        );
      },
      "\\color{(...)}0": function color0(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "\\color{",
          "",
          "",
          "}"
        );
      },
      "\\color{(...)}{(...)}1": function color1(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "\\color{",
          "",
          "",
          "}",
          "{",
          "",
          "",
          "}"
        );
      },
      "\\color(...){(...)}2": function color2(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "\\color",
          "\\",
          "",
          /^(?=\{)/,
          "{",
          "",
          "",
          "}"
        );
      },
      "\\ce{(...)}": function ce(input) {
        return mhchemParser.patterns.findObserveGroups(
          input,
          "\\ce{",
          "",
          "",
          "}"
        );
      },
      oxidation$: /^(?:[+-][IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/,
      "d-oxidation$": /^(?:[+-]?\s?[IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/,
      // 0 could be oxidation or charge
      "roman numeral": /^[IVX]+/,
      "1/2$": /^[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+(?:\$[a-z]\$|[a-z])?$/,
      amount: function amount(input) {
        var match; // e.g. 2, 0.5, 1/2, -2, n/2, +;  $a$ could be added later in parsing

        match = input.match(
          /^(?:(?:(?:\([+\-]?[0-9]+\/[0-9]+\)|[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+|[+\-]?[0-9]+[.,][0-9]+|[+\-]?\.[0-9]+|[+\-]?[0-9]+)(?:[a-z](?=\s*[A-Z]))?)|[+\-]?[a-z](?=\s*[A-Z])|\+(?!\s))/
        );

        if (match) {
          return {
            match_: match[0],
            remainder: input.substr(match[0].length),
          };
        }

        var a = mhchemParser.patterns.findObserveGroups(
          input,
          "",
          "$",
          "$",
          ""
        );

        if (a) {
          // e.g. $2n-1$, $-$
          match = a.match_.match(
            /^\$(?:\(?[+\-]?(?:[0-9]*[a-z]?[+\-])?[0-9]*[a-z](?:[+\-][0-9]*[a-z]?)?\)?|\+|-)\$$/
          );

          if (match) {
            return {
              match_: match[0],
              remainder: input.substr(match[0].length),
            };
          }
        }

        return null;
      },
      amount2: function amount2(input) {
        return this["amount"](input);
      },
      "(KV letters),": /^(?:[A-Z][a-z]{0,2}|i)(?=,)/,
      formula$: function formula$(input) {
        if (input.match(/^\([a-z]+\)$/)) {
          return null;
        } // state of aggregation = no formula

        var match = input.match(
          /^(?:[a-z]|(?:[0-9\ \+\-\,\.\(\)]+[a-z])+[0-9\ \+\-\,\.\(\)]*|(?:[a-z][0-9\ \+\-\,\.\(\)]+)+[a-z]?)$/
        );

        if (match) {
          return {
            match_: match[0],
            remainder: input.substr(match[0].length),
          };
        }

        return null;
      },
      uprightEntities: /^(?:pH|pOH|pC|pK|iPr|iBu)(?=$|[^a-zA-Z])/,
      "/": /^\s*(\/)\s*/,
      "//": /^\s*(\/\/)\s*/,
      "*": /^\s*[*.]\s*/,
    },
    findObserveGroups: function findObserveGroups(
      input,
      begExcl,
      begIncl,
      endIncl,
      endExcl,
      beg2Excl,
      beg2Incl,
      end2Incl,
      end2Excl,
      combine
    ) {
      /** @type {{(input: string, pattern: string | RegExp): string | string[] | null;}} */
      var _match = function _match(input, pattern) {
        if (typeof pattern === "string") {
          if (input.indexOf(pattern) !== 0) {
            return null;
          }

          return pattern;
        } else {
          var match = input.match(pattern);

          if (!match) {
            return null;
          }

          return match[0];
        }
      };
      /** @type {{(input: string, i: number, endChars: string | RegExp): {endMatchBegin: number, endMatchEnd: number} | null;}} */

      var _findObserveGroups = function _findObserveGroups(input, i, endChars) {
        var braces = 0;

        while (i < input.length) {
          var a = input.charAt(i);

          var match = _match(input.substr(i), endChars);

          if (match !== null && braces === 0) {
            return {
              endMatchBegin: i,
              endMatchEnd: i + match.length,
            };
          } else if (a === "{") {
            braces++;
          } else if (a === "}") {
            if (braces === 0) {
              throw [
                "ExtraCloseMissingOpen",
                "Extra close brace or missing open brace",
              ];
            } else {
              braces--;
            }
          }

          i++;
        }

        if (braces > 0) {
          return null;
        }

        return null;
      };

      var match = _match(input, begExcl);

      if (match === null) {
        return null;
      }

      input = input.substr(match.length);
      match = _match(input, begIncl);

      if (match === null) {
        return null;
      }

      var e = _findObserveGroups(input, match.length, endIncl || endExcl);

      if (e === null) {
        return null;
      }

      var match1 = input.substring(
        0,
        endIncl ? e.endMatchEnd : e.endMatchBegin
      );

      if (!(beg2Excl || beg2Incl)) {
        return {
          match_: match1,
          remainder: input.substr(e.endMatchEnd),
        };
      } else {
        var group2 = this.findObserveGroups(
          input.substr(e.endMatchEnd),
          beg2Excl,
          beg2Incl,
          end2Incl,
          end2Excl
        );

        if (group2 === null) {
          return null;
        }
        /** @type {string[]} */

        var matchRet = [match1, group2.match_];
        return {
          match_: combine ? matchRet.join("") : matchRet,
          remainder: group2.remainder,
        };
      }
    },
    //
    // Matching function
    // e.g. match("a", input) will look for the regexp called "a" and see if it matches
    // returns null or {match_:"a", remainder:"bc"}
    //
    match_: function match_(m, input) {
      var pattern = mhchemParser.patterns.patterns[m];

      if (pattern === undefined) {
        throw ["MhchemBugP", "mhchem bug P. Please report. (" + m + ")"]; // Trying to use non-existing pattern
      } else if (typeof pattern === "function") {
        return mhchemParser.patterns.patterns[m](input); // cannot use cached var pattern here, because some pattern functions need this===mhchemParser
      } else {
        // RegExp
        var match = input.match(pattern);

        if (match) {
          var mm;

          if (match[2]) {
            mm = [match[1], match[2]];
          } else if (match[1]) {
            mm = match[1];
          } else {
            mm = match[0];
          }

          return {
            match_: mm,
            remainder: input.substr(match[0].length),
          };
        }

        return null;
      }
    },
  },
  //
  // Generic state machine actions
  //
  actions: {
    "a=": function a(buffer, m) {
      buffer.a = (buffer.a || "") + m;
    },
    "b=": function b(buffer, m) {
      buffer.b = (buffer.b || "") + m;
    },
    "p=": function p(buffer, m) {
      buffer.p = (buffer.p || "") + m;
    },
    "o=": function o(buffer, m) {
      buffer.o = (buffer.o || "") + m;
    },
    "q=": function q(buffer, m) {
      buffer.q = (buffer.q || "") + m;
    },
    "d=": function d(buffer, m) {
      buffer.d = (buffer.d || "") + m;
    },
    "rm=": function rm(buffer, m) {
      buffer.rm = (buffer.rm || "") + m;
    },
    "text=": function text(buffer, m) {
      buffer.text_ = (buffer.text_ || "") + m;
    },
    insert: function insert(buffer, m, a) {
      return {
        type_: a,
      };
    },
    "insert+p1": function insertP1(buffer, m, a) {
      return {
        type_: a,
        p1: m,
      };
    },
    "insert+p1+p2": function insertP1P2(buffer, m, a) {
      return {
        type_: a,
        p1: m[0],
        p2: m[1],
      };
    },
    copy: function copy(buffer, m) {
      return m;
    },
    rm: function rm(buffer, m) {
      return {
        type_: "rm",
        p1: m || "",
      };
    },
    text: function text(buffer, m) {
      return mhchemParser.go(m, "text");
    },
    "{text}": function text(buffer, m) {
      var ret = ["{"];
      mhchemParser.concatArray(ret, mhchemParser.go(m, "text"));
      ret.push("}");
      return ret;
    },
    "tex-math": function texMath(buffer, m) {
      return mhchemParser.go(m, "tex-math");
    },
    "tex-math tight": function texMathTight(buffer, m) {
      return mhchemParser.go(m, "tex-math tight");
    },
    bond: function bond(buffer, m, k) {
      return {
        type_: "bond",
        kind_: k || m,
      };
    },
    "color0-output": function color0Output(buffer, m) {
      return {
        type_: "color0",
        color: m[0],
      };
    },
    ce: function ce(buffer, m) {
      return mhchemParser.go(m);
    },
    "1/2": function _(buffer, m) {
      /** @type {ParserOutput[]} */
      var ret = [];

      if (m.match(/^[+\-]/)) {
        ret.push(m.substr(0, 1));
        m = m.substr(1);
      }

      var n = m.match(/^([0-9]+|\$[a-z]\$|[a-z])\/([0-9]+)(\$[a-z]\$|[a-z])?$/);
      n[1] = n[1].replace(/\$/g, "");
      ret.push({
        type_: "frac",
        p1: n[1],
        p2: n[2],
      });

      if (n[3]) {
        n[3] = n[3].replace(/\$/g, "");
        ret.push({
          type_: "tex-math",
          p1: n[3],
        });
      }

      return ret;
    },
    "9,9": function _(buffer, m) {
      return mhchemParser.go(m, "9,9");
    },
  },
  //
  // createTransitions
  // convert  { 'letter': { 'state': { action_: 'output' } } }  to  { 'state' => [ { pattern: 'letter', task: { action_: [{type_: 'output'}] } } ] }
  // with expansion of 'a|b' to 'a' and 'b' (at 2 places)
  //
  createTransitions: function createTransitions(o) {
    var pattern, state;
    /** @type {string[]} */

    var stateArray;
    var i; //
    // 1. Collect all states
    //

    /** @type {Transitions} */

    var transitions = {};

    for (pattern in o) {
      for (state in o[pattern]) {
        stateArray = state.split("|");
        o[pattern][state].stateArray = stateArray;

        for (i = 0; i < stateArray.length; i++) {
          transitions[stateArray[i]] = [];
        }
      }
    } //
    // 2. Fill states
    //

    for (pattern in o) {
      for (state in o[pattern]) {
        stateArray = o[pattern][state].stateArray || [];

        for (i = 0; i < stateArray.length; i++) {
          //
          // 2a. Normalize actions into array:  'text=' ==> [{type_:'text='}]
          // (Note to myself: Resolving the function here would be problematic. It would need .bind (for *this*) and currying (for *option*).)
          //

          /** @type {any} */
          var p = o[pattern][state];

          if (p.action_) {
            p.action_ = [].concat(p.action_);

            for (var k = 0; k < p.action_.length; k++) {
              if (typeof p.action_[k] === "string") {
                p.action_[k] = {
                  type_: p.action_[k],
                };
              }
            }
          } else {
            p.action_ = [];
          } //
          // 2.b Multi-insert
          //

          var patternArray = pattern.split("|");

          for (var j = 0; j < patternArray.length; j++) {
            if (stateArray[i] === "*") {
              // insert into all
              for (var t in transitions) {
                transitions[t].push({
                  pattern: patternArray[j],
                  task: p,
                });
              }
            } else {
              transitions[stateArray[i]].push({
                pattern: patternArray[j],
                task: p,
              });
            }
          }
        }
      }
    }

    return transitions;
  },
  stateMachines: {},
}; //
// Definition of state machines
//

mhchemParser.stateMachines = {
  //
  // \ce state machines
  //
  //#region ce
  ce: {
    // main parser
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": {
          action_: "output",
        },
      },
      else: {
        "0|1|2": {
          action_: "beginsWithBond=false",
          revisit: true,
          toContinue: true,
        },
      },
      oxidation$: {
        0: {
          action_: "oxidation-output",
        },
      },
      CMT: {
        r: {
          action_: "rdt=",
          nextState: "rt",
        },
        rd: {
          action_: "rqt=",
          nextState: "rdt",
        },
      },
      arrowUpDown: {
        "0|1|2|as": {
          action_: ["sb=false", "output", "operator"],
          nextState: "1",
        },
      },
      uprightEntities: {
        "0|1|2": {
          action_: ["o=", "output"],
          nextState: "1",
        },
      },
      orbital: {
        "0|1|2|3": {
          action_: "o=",
          nextState: "o",
        },
      },
      "->": {
        "0|1|2|3": {
          action_: "r=",
          nextState: "r",
        },
        "a|as": {
          action_: ["output", "r="],
          nextState: "r",
        },
        "*": {
          action_: ["output", "r="],
          nextState: "r",
        },
      },
      "+": {
        o: {
          action_: "d= kv",
          nextState: "d",
        },
        "d|D": {
          action_: "d=",
          nextState: "d",
        },
        q: {
          action_: "d=",
          nextState: "qd",
        },
        "qd|qD": {
          action_: "d=",
          nextState: "qd",
        },
        dq: {
          action_: ["output", "d="],
          nextState: "d",
        },
        3: {
          action_: ["sb=false", "output", "operator"],
          nextState: "0",
        },
      },
      amount: {
        "0|2": {
          action_: "a=",
          nextState: "a",
        },
      },
      "pm-operator": {
        "0|1|2|a|as": {
          action_: [
            "sb=false",
            "output",
            {
              type_: "operator",
              option: "\\pm",
            },
          ],
          nextState: "0",
        },
      },
      operator: {
        "0|1|2|a|as": {
          action_: ["sb=false", "output", "operator"],
          nextState: "0",
        },
      },
      "-$": {
        "o|q": {
          action_: ["charge or bond", "output"],
          nextState: "qd",
        },
        d: {
          action_: "d=",
          nextState: "d",
        },
        D: {
          action_: [
            "output",
            {
              type_: "bond",
              option: "-",
            },
          ],
          nextState: "3",
        },
        q: {
          action_: "d=",
          nextState: "qd",
        },
        qd: {
          action_: "d=",
          nextState: "qd",
        },
        "qD|dq": {
          action_: [
            "output",
            {
              type_: "bond",
              option: "-",
            },
          ],
          nextState: "3",
        },
      },
      "-9": {
        "3|o": {
          action_: [
            "output",
            {
              type_: "insert",
              option: "hyphen",
            },
          ],
          nextState: "3",
        },
      },
      "- orbital overlap": {
        o: {
          action_: [
            "output",
            {
              type_: "insert",
              option: "hyphen",
            },
          ],
          nextState: "2",
        },
        d: {
          action_: [
            "output",
            {
              type_: "insert",
              option: "hyphen",
            },
          ],
          nextState: "2",
        },
      },
      "-": {
        "0|1|2": {
          action_: [
            {
              type_: "output",
              option: 1,
            },
            "beginsWithBond=true",
            {
              type_: "bond",
              option: "-",
            },
          ],
          nextState: "3",
        },
        3: {
          action_: {
            type_: "bond",
            option: "-",
          },
        },
        a: {
          action_: [
            "output",
            {
              type_: "insert",
              option: "hyphen",
            },
          ],
          nextState: "2",
        },
        as: {
          action_: [
            {
              type_: "output",
              option: 2,
            },
            {
              type_: "bond",
              option: "-",
            },
          ],
          nextState: "3",
        },
        b: {
          action_: "b=",
        },
        o: {
          action_: {
            type_: "- after o/d",
            option: false,
          },
          nextState: "2",
        },
        q: {
          action_: {
            type_: "- after o/d",
            option: false,
          },
          nextState: "2",
        },
        "d|qd|dq": {
          action_: {
            type_: "- after o/d",
            option: true,
          },
          nextState: "2",
        },
        "D|qD|p": {
          action_: [
            "output",
            {
              type_: "bond",
              option: "-",
            },
          ],
          nextState: "3",
        },
      },
      amount2: {
        "1|3": {
          action_: "a=",
          nextState: "a",
        },
      },
      letters: {
        "0|1|2|3|a|as|b|p|bp|o": {
          action_: "o=",
          nextState: "o",
        },
        "q|dq": {
          action_: ["output", "o="],
          nextState: "o",
        },
        "d|D|qd|qD": {
          action_: "o after d",
          nextState: "o",
        },
      },
      digits: {
        o: {
          action_: "q=",
          nextState: "q",
        },
        "d|D": {
          action_: "q=",
          nextState: "dq",
        },
        q: {
          action_: ["output", "o="],
          nextState: "o",
        },
        a: {
          action_: "o=",
          nextState: "o",
        },
      },
      "space A": {
        "b|p|bp": {},
      },
      space: {
        a: {
          nextState: "as",
        },
        0: {
          action_: "sb=false",
        },
        "1|2": {
          action_: "sb=true",
        },
        "r|rt|rd|rdt|rdq": {
          action_: "output",
          nextState: "0",
        },
        "*": {
          action_: ["output", "sb=true"],
          nextState: "1",
        },
      },
      "1st-level escape": {
        "1|2": {
          action_: [
            "output",
            {
              type_: "insert+p1",
              option: "1st-level escape",
            },
          ],
        },
        "*": {
          action_: [
            "output",
            {
              type_: "insert+p1",
              option: "1st-level escape",
            },
          ],
          nextState: "0",
        },
      },
      "[(...)]": {
        "r|rt": {
          action_: "rd=",
          nextState: "rd",
        },
        "rd|rdt": {
          action_: "rq=",
          nextState: "rdq",
        },
      },
      "...": {
        "o|d|D|dq|qd|qD": {
          action_: [
            "output",
            {
              type_: "bond",
              option: "...",
            },
          ],
          nextState: "3",
        },
        "*": {
          action_: [
            {
              type_: "output",
              option: 1,
            },
            {
              type_: "insert",
              option: "ellipsis",
            },
          ],
          nextState: "1",
        },
      },
      ". |* ": {
        "*": {
          action_: [
            "output",
            {
              type_: "insert",
              option: "addition compound",
            },
          ],
          nextState: "1",
        },
      },
      "state of aggregation $": {
        "*": {
          action_: ["output", "state of aggregation"],
          nextState: "1",
        },
      },
      "{[(": {
        "a|as|o": {
          action_: ["o=", "output", "parenthesisLevel++"],
          nextState: "2",
        },
        "0|1|2|3": {
          action_: ["o=", "output", "parenthesisLevel++"],
          nextState: "2",
        },
        "*": {
          action_: ["output", "o=", "output", "parenthesisLevel++"],
          nextState: "2",
        },
      },
      ")]}": {
        "0|1|2|3|b|p|bp|o": {
          action_: ["o=", "parenthesisLevel--"],
          nextState: "o",
        },
        "a|as|d|D|q|qd|qD|dq": {
          action_: ["output", "o=", "parenthesisLevel--"],
          nextState: "o",
        },
      },
      ", ": {
        "*": {
          action_: ["output", "comma"],
          nextState: "0",
        },
      },
      "^_": {
        // ^ and _ without a sensible argument
        "*": {},
      },
      "^{(...)}|^($...$)": {
        "0|1|2|as": {
          action_: "b=",
          nextState: "b",
        },
        p: {
          action_: "b=",
          nextState: "bp",
        },
        "3|o": {
          action_: "d= kv",
          nextState: "D",
        },
        q: {
          action_: "d=",
          nextState: "qD",
        },
        "d|D|qd|qD|dq": {
          action_: ["output", "d="],
          nextState: "D",
        },
      },
      "^a|^\\x{}{}|^\\x{}|^\\x|'": {
        "0|1|2|as": {
          action_: "b=",
          nextState: "b",
        },
        p: {
          action_: "b=",
          nextState: "bp",
        },
        "3|o": {
          action_: "d= kv",
          nextState: "d",
        },
        q: {
          action_: "d=",
          nextState: "qd",
        },
        "d|qd|D|qD": {
          action_: "d=",
        },
        dq: {
          action_: ["output", "d="],
          nextState: "d",
        },
      },
      "_{(state of aggregation)}$": {
        "d|D|q|qd|qD|dq": {
          action_: ["output", "q="],
          nextState: "q",
        },
      },
      "_{(...)}|_($...$)|_9|_\\x{}{}|_\\x{}|_\\x": {
        "0|1|2|as": {
          action_: "p=",
          nextState: "p",
        },
        b: {
          action_: "p=",
          nextState: "bp",
        },
        "3|o": {
          action_: "q=",
          nextState: "q",
        },
        "d|D": {
          action_: "q=",
          nextState: "dq",
        },
        "q|qd|qD|dq": {
          action_: ["output", "q="],
          nextState: "q",
        },
      },
      "=<>": {
        "0|1|2|3|a|as|o|q|d|D|qd|qD|dq": {
          action_: [
            {
              type_: "output",
              option: 2,
            },
            "bond",
          ],
          nextState: "3",
        },
      },
      "#": {
        "0|1|2|3|a|as|o": {
          action_: [
            {
              type_: "output",
              option: 2,
            },
            {
              type_: "bond",
              option: "#",
            },
          ],
          nextState: "3",
        },
      },
      "{}": {
        "*": {
          action_: {
            type_: "output",
            option: 1,
          },
          nextState: "1",
        },
      },
      "{...}": {
        "0|1|2|3|a|as|b|p|bp": {
          action_: "o=",
          nextState: "o",
        },
        "o|d|D|q|qd|qD|dq": {
          action_: ["output", "o="],
          nextState: "o",
        },
      },
      "$...$": {
        a: {
          action_: "a=",
        },
        // 2$n$
        "0|1|2|3|as|b|p|bp|o": {
          action_: "o=",
          nextState: "o",
        },
        // not 'amount'
        "as|o": {
          action_: "o=",
        },
        "q|d|D|qd|qD|dq": {
          action_: ["output", "o="],
          nextState: "o",
        },
      },
      "\\bond{(...)}": {
        "*": {
          action_: [
            {
              type_: "output",
              option: 2,
            },
            "bond",
          ],
          nextState: "3",
        },
      },
      "\\frac{(...)}": {
        "*": {
          action_: [
            {
              type_: "output",
              option: 1,
            },
            "frac-output",
          ],
          nextState: "3",
        },
      },
      "\\overset{(...)}": {
        "*": {
          action_: [
            {
              type_: "output",
              option: 2,
            },
            "overset-output",
          ],
          nextState: "3",
        },
      },
      "\\underset{(...)}": {
        "*": {
          action_: [
            {
              type_: "output",
              option: 2,
            },
            "underset-output",
          ],
          nextState: "3",
        },
      },
      "\\underbrace{(...)}": {
        "*": {
          action_: [
            {
              type_: "output",
              option: 2,
            },
            "underbrace-output",
          ],
          nextState: "3",
        },
      },
      "\\color{(...)}{(...)}1|\\color(...){(...)}2": {
        "*": {
          action_: [
            {
              type_: "output",
              option: 2,
            },
            "color-output",
          ],
          nextState: "3",
        },
      },
      "\\color{(...)}0": {
        "*": {
          action_: [
            {
              type_: "output",
              option: 2,
            },
            "color0-output",
          ],
        },
      },
      "\\ce{(...)}": {
        "*": {
          action_: [
            {
              type_: "output",
              option: 2,
            },
            "ce",
          ],
          nextState: "3",
        },
      },
      "\\,": {
        "*": {
          action_: [
            {
              type_: "output",
              option: 1,
            },
            "copy",
          ],
          nextState: "1",
        },
      },
      "\\x{}{}|\\x{}|\\x": {
        "0|1|2|3|a|as|b|p|bp|o|c0": {
          action_: ["o=", "output"],
          nextState: "3",
        },
        "*": {
          action_: ["output", "o=", "output"],
          nextState: "3",
        },
      },
      others: {
        "*": {
          action_: [
            {
              type_: "output",
              option: 1,
            },
            "copy",
          ],
          nextState: "3",
        },
      },
      else2: {
        a: {
          action_: "a to o",
          nextState: "o",
          revisit: true,
        },
        as: {
          action_: ["output", "sb=true"],
          nextState: "1",
          revisit: true,
        },
        "r|rt|rd|rdt|rdq": {
          action_: ["output"],
          nextState: "0",
          revisit: true,
        },
        "*": {
          action_: ["output", "copy"],
          nextState: "3",
        },
      },
    }),
    actions: {
      "o after d": function oAfterD(buffer, m) {
        var ret;

        if ((buffer.d || "").match(/^[0-9]+$/)) {
          var tmp = buffer.d;
          buffer.d = undefined;
          ret = this["output"](buffer);
          buffer.b = tmp;
        } else {
          ret = this["output"](buffer);
        }

        mhchemParser.actions["o="](buffer, m);
        return ret;
      },
      "d= kv": function dKv(buffer, m) {
        buffer.d = m;
        buffer.dType = "kv";
      },
      "charge or bond": function chargeOrBond(buffer, m) {
        if (buffer["beginsWithBond"]) {
          /** @type {ParserOutput[]} */
          var ret = [];
          mhchemParser.concatArray(ret, this["output"](buffer));
          mhchemParser.concatArray(
            ret,
            mhchemParser.actions["bond"](buffer, m, "-")
          );
          return ret;
        } else {
          buffer.d = m;
        }
      },
      "- after o/d": function afterOD(buffer, m, isAfterD) {
        var c1 = mhchemParser.patterns.match_("orbital", buffer.o || "");
        var c2 = mhchemParser.patterns.match_(
          "one lowercase greek letter $",
          buffer.o || ""
        );
        var c3 = mhchemParser.patterns.match_(
          "one lowercase latin letter $",
          buffer.o || ""
        );
        var c4 = mhchemParser.patterns.match_(
          "$one lowercase latin letter$ $",
          buffer.o || ""
        );
        var hyphenFollows =
          m === "-" && ((c1 && c1.remainder === "") || c2 || c3 || c4);

        if (
          hyphenFollows &&
          !buffer.a &&
          !buffer.b &&
          !buffer.p &&
          !buffer.d &&
          !buffer.q &&
          !c1 &&
          c3
        ) {
          buffer.o = "$" + buffer.o + "$";
        }
        /** @type {ParserOutput[]} */

        var ret = [];

        if (hyphenFollows) {
          mhchemParser.concatArray(ret, this["output"](buffer));
          ret.push({
            type_: "hyphen",
          });
        } else {
          c1 = mhchemParser.patterns.match_("digits", buffer.d || "");

          if (isAfterD && c1 && c1.remainder === "") {
            mhchemParser.concatArray(
              ret,
              mhchemParser.actions["d="](buffer, m)
            );
            mhchemParser.concatArray(ret, this["output"](buffer));
          } else {
            mhchemParser.concatArray(ret, this["output"](buffer));
            mhchemParser.concatArray(
              ret,
              mhchemParser.actions["bond"](buffer, m, "-")
            );
          }
        }

        return ret;
      },
      "a to o": function aToO(buffer) {
        buffer.o = buffer.a;
        buffer.a = undefined;
      },
      "sb=true": function sbTrue(buffer) {
        buffer.sb = true;
      },
      "sb=false": function sbFalse(buffer) {
        buffer.sb = false;
      },
      "beginsWithBond=true": function beginsWithBondTrue(buffer) {
        buffer["beginsWithBond"] = true;
      },
      "beginsWithBond=false": function beginsWithBondFalse(buffer) {
        buffer["beginsWithBond"] = false;
      },
      "parenthesisLevel++": function parenthesisLevel(buffer) {
        buffer["parenthesisLevel"]++;
      },
      "parenthesisLevel--": function parenthesisLevel(buffer) {
        buffer["parenthesisLevel"]--;
      },
      "state of aggregation": function stateOfAggregation(buffer, m) {
        return {
          type_: "state of aggregation",
          p1: mhchemParser.go(m, "o"),
        };
      },
      comma: function comma(buffer, m) {
        var a = m.replace(/\s*$/, "");
        var withSpace = a !== m;

        if (withSpace && buffer["parenthesisLevel"] === 0) {
          return {
            type_: "comma enumeration L",
            p1: a,
          };
        } else {
          return {
            type_: "comma enumeration M",
            p1: a,
          };
        }
      },
      output: function output(buffer, m, entityFollows) {
        // entityFollows:
        //   undefined = if we have nothing else to output, also ignore the just read space (buffer.sb)
        //   1 = an entity follows, never omit the space if there was one just read before (can only apply to state 1)
        //   2 = 1 + the entity can have an amount, so output a\, instead of converting it to o (can only apply to states a|as)

        /** @type {ParserOutput | ParserOutput[]} */
        var ret;

        if (!buffer.r) {
          ret = [];

          if (
            !buffer.a &&
            !buffer.b &&
            !buffer.p &&
            !buffer.o &&
            !buffer.q &&
            !buffer.d &&
            !entityFollows
          );
          else {
            if (buffer.sb) {
              ret.push({
                type_: "entitySkip",
              });
            }

            if (
              !buffer.o &&
              !buffer.q &&
              !buffer.d &&
              !buffer.b &&
              !buffer.p &&
              entityFollows !== 2
            ) {
              buffer.o = buffer.a;
              buffer.a = undefined;
            } else if (
              !buffer.o &&
              !buffer.q &&
              !buffer.d &&
              (buffer.b || buffer.p)
            ) {
              buffer.o = buffer.a;
              buffer.d = buffer.b;
              buffer.q = buffer.p;
              buffer.a = buffer.b = buffer.p = undefined;
            } else {
              if (
                buffer.o &&
                buffer.dType === "kv" &&
                mhchemParser.patterns.match_("d-oxidation$", buffer.d || "")
              ) {
                buffer.dType = "oxidation";
              } else if (buffer.o && buffer.dType === "kv" && !buffer.q) {
                buffer.dType = undefined;
              }
            }

            ret.push({
              type_: "chemfive",
              a: mhchemParser.go(buffer.a, "a"),
              b: mhchemParser.go(buffer.b, "bd"),
              p: mhchemParser.go(buffer.p, "pq"),
              o: mhchemParser.go(buffer.o, "o"),
              q: mhchemParser.go(buffer.q, "pq"),
              d: mhchemParser.go(
                buffer.d,
                buffer.dType === "oxidation" ? "oxidation" : "bd"
              ),
              dType: buffer.dType,
            });
          }
        } else {
          // r

          /** @type {ParserOutput[]} */
          var rd;

          if (buffer.rdt === "M") {
            rd = mhchemParser.go(buffer.rd, "tex-math");
          } else if (buffer.rdt === "T") {
            rd = [
              {
                type_: "text",
                p1: buffer.rd || "",
              },
            ];
          } else {
            rd = mhchemParser.go(buffer.rd);
          }
          /** @type {ParserOutput[]} */

          var rq;

          if (buffer.rqt === "M") {
            rq = mhchemParser.go(buffer.rq, "tex-math");
          } else if (buffer.rqt === "T") {
            rq = [
              {
                type_: "text",
                p1: buffer.rq || "",
              },
            ];
          } else {
            rq = mhchemParser.go(buffer.rq);
          }

          ret = {
            type_: "arrow",
            r: buffer.r,
            rd: rd,
            rq: rq,
          };
        }

        for (var p in buffer) {
          if (p !== "parenthesisLevel" && p !== "beginsWithBond") {
            delete buffer[p];
          }
        }

        return ret;
      },
      "oxidation-output": function oxidationOutput(buffer, m) {
        var ret = ["{"];
        mhchemParser.concatArray(ret, mhchemParser.go(m, "oxidation"));
        ret.push("}");
        return ret;
      },
      "frac-output": function fracOutput(buffer, m) {
        return {
          type_: "frac-ce",
          p1: mhchemParser.go(m[0]),
          p2: mhchemParser.go(m[1]),
        };
      },
      "overset-output": function oversetOutput(buffer, m) {
        return {
          type_: "overset",
          p1: mhchemParser.go(m[0]),
          p2: mhchemParser.go(m[1]),
        };
      },
      "underset-output": function undersetOutput(buffer, m) {
        return {
          type_: "underset",
          p1: mhchemParser.go(m[0]),
          p2: mhchemParser.go(m[1]),
        };
      },
      "underbrace-output": function underbraceOutput(buffer, m) {
        return {
          type_: "underbrace",
          p1: mhchemParser.go(m[0]),
          p2: mhchemParser.go(m[1]),
        };
      },
      "color-output": function colorOutput(buffer, m) {
        return {
          type_: "color",
          color1: m[0],
          color2: mhchemParser.go(m[1]),
        };
      },
      "r=": function r(buffer, m) {
        buffer.r = m;
      },
      "rdt=": function rdt(buffer, m) {
        buffer.rdt = m;
      },
      "rd=": function rd(buffer, m) {
        buffer.rd = m;
      },
      "rqt=": function rqt(buffer, m) {
        buffer.rqt = m;
      },
      "rq=": function rq(buffer, m) {
        buffer.rq = m;
      },
      operator: function operator(buffer, m, p1) {
        return {
          type_: "operator",
          kind_: p1 || m,
        };
      },
    },
  },
  a: {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": {},
      },
      "1/2$": {
        0: {
          action_: "1/2",
        },
      },
      else: {
        0: {
          nextState: "1",
          revisit: true,
        },
      },
      "$(...)$": {
        "*": {
          action_: "tex-math tight",
          nextState: "1",
        },
      },
      ",": {
        "*": {
          action_: {
            type_: "insert",
            option: "commaDecimal",
          },
        },
      },
      else2: {
        "*": {
          action_: "copy",
        },
      },
    }),
    actions: {},
  },
  o: {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": {},
      },
      "1/2$": {
        0: {
          action_: "1/2",
        },
      },
      else: {
        0: {
          nextState: "1",
          revisit: true,
        },
      },
      letters: {
        "*": {
          action_: "rm",
        },
      },
      "\\ca": {
        "*": {
          action_: {
            type_: "insert",
            option: "circa",
          },
        },
      },
      "\\x{}{}|\\x{}|\\x": {
        "*": {
          action_: "copy",
        },
      },
      "${(...)}$|$(...)$": {
        "*": {
          action_: "tex-math",
        },
      },
      "{(...)}": {
        "*": {
          action_: "{text}",
        },
      },
      else2: {
        "*": {
          action_: "copy",
        },
      },
    }),
    actions: {},
  },
  text: {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": {
          action_: "output",
        },
      },
      "{...}": {
        "*": {
          action_: "text=",
        },
      },
      "${(...)}$|$(...)$": {
        "*": {
          action_: "tex-math",
        },
      },
      "\\greek": {
        "*": {
          action_: ["output", "rm"],
        },
      },
      "\\,|\\x{}{}|\\x{}|\\x": {
        "*": {
          action_: ["output", "copy"],
        },
      },
      else: {
        "*": {
          action_: "text=",
        },
      },
    }),
    actions: {
      output: function output(buffer) {
        if (buffer.text_) {
          /** @type {ParserOutput} */
          var ret = {
            type_: "text",
            p1: buffer.text_,
          };

          for (var p in buffer) {
            delete buffer[p];
          }

          return ret;
        }
      },
    },
  },
  pq: {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": {},
      },
      "state of aggregation $": {
        "*": {
          action_: "state of aggregation",
        },
      },
      i$: {
        0: {
          nextState: "!f",
          revisit: true,
        },
      },
      "(KV letters),": {
        0: {
          action_: "rm",
          nextState: "0",
        },
      },
      formula$: {
        0: {
          nextState: "f",
          revisit: true,
        },
      },
      "1/2$": {
        0: {
          action_: "1/2",
        },
      },
      else: {
        0: {
          nextState: "!f",
          revisit: true,
        },
      },
      "${(...)}$|$(...)$": {
        "*": {
          action_: "tex-math",
        },
      },
      "{(...)}": {
        "*": {
          action_: "text",
        },
      },
      "a-z": {
        f: {
          action_: "tex-math",
        },
      },
      letters: {
        "*": {
          action_: "rm",
        },
      },
      "-9.,9": {
        "*": {
          action_: "9,9",
        },
      },
      ",": {
        "*": {
          action_: {
            type_: "insert+p1",
            option: "comma enumeration S",
          },
        },
      },
      "\\color{(...)}{(...)}1|\\color(...){(...)}2": {
        "*": {
          action_: "color-output",
        },
      },
      "\\color{(...)}0": {
        "*": {
          action_: "color0-output",
        },
      },
      "\\ce{(...)}": {
        "*": {
          action_: "ce",
        },
      },
      "\\,|\\x{}{}|\\x{}|\\x": {
        "*": {
          action_: "copy",
        },
      },
      else2: {
        "*": {
          action_: "copy",
        },
      },
    }),
    actions: {
      "state of aggregation": function stateOfAggregation(buffer, m) {
        return {
          type_: "state of aggregation subscript",
          p1: mhchemParser.go(m, "o"),
        };
      },
      "color-output": function colorOutput(buffer, m) {
        return {
          type_: "color",
          color1: m[0],
          color2: mhchemParser.go(m[1], "pq"),
        };
      },
    },
  },
  bd: {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": {},
      },
      x$: {
        0: {
          nextState: "!f",
          revisit: true,
        },
      },
      formula$: {
        0: {
          nextState: "f",
          revisit: true,
        },
      },
      else: {
        0: {
          nextState: "!f",
          revisit: true,
        },
      },
      "-9.,9 no missing 0": {
        "*": {
          action_: "9,9",
        },
      },
      ".": {
        "*": {
          action_: {
            type_: "insert",
            option: "electron dot",
          },
        },
      },
      "a-z": {
        f: {
          action_: "tex-math",
        },
      },
      x: {
        "*": {
          action_: {
            type_: "insert",
            option: "KV x",
          },
        },
      },
      letters: {
        "*": {
          action_: "rm",
        },
      },
      "'": {
        "*": {
          action_: {
            type_: "insert",
            option: "prime",
          },
        },
      },
      "${(...)}$|$(...)$": {
        "*": {
          action_: "tex-math",
        },
      },
      "{(...)}": {
        "*": {
          action_: "text",
        },
      },
      "\\color{(...)}{(...)}1|\\color(...){(...)}2": {
        "*": {
          action_: "color-output",
        },
      },
      "\\color{(...)}0": {
        "*": {
          action_: "color0-output",
        },
      },
      "\\ce{(...)}": {
        "*": {
          action_: "ce",
        },
      },
      "\\,|\\x{}{}|\\x{}|\\x": {
        "*": {
          action_: "copy",
        },
      },
      else2: {
        "*": {
          action_: "copy",
        },
      },
    }),
    actions: {
      "color-output": function colorOutput(buffer, m) {
        return {
          type_: "color",
          color1: m[0],
          color2: mhchemParser.go(m[1], "bd"),
        };
      },
    },
  },
  oxidation: {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": {},
      },
      "roman numeral": {
        "*": {
          action_: "roman-numeral",
        },
      },
      "${(...)}$|$(...)$": {
        "*": {
          action_: "tex-math",
        },
      },
      else: {
        "*": {
          action_: "copy",
        },
      },
    }),
    actions: {
      "roman-numeral": function romanNumeral(buffer, m) {
        return {
          type_: "roman numeral",
          p1: m || "",
        };
      },
    },
  },
  "tex-math": {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": {
          action_: "output",
        },
      },
      "\\ce{(...)}": {
        "*": {
          action_: ["output", "ce"],
        },
      },
      "{...}|\\,|\\x{}{}|\\x{}|\\x": {
        "*": {
          action_: "o=",
        },
      },
      else: {
        "*": {
          action_: "o=",
        },
      },
    }),
    actions: {
      output: function output(buffer) {
        if (buffer.o) {
          /** @type {ParserOutput} */
          var ret = {
            type_: "tex-math",
            p1: buffer.o,
          };

          for (var p in buffer) {
            delete buffer[p];
          }

          return ret;
        }
      },
    },
  },
  "tex-math tight": {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": {
          action_: "output",
        },
      },
      "\\ce{(...)}": {
        "*": {
          action_: ["output", "ce"],
        },
      },
      "{...}|\\,|\\x{}{}|\\x{}|\\x": {
        "*": {
          action_: "o=",
        },
      },
      "-|+": {
        "*": {
          action_: "tight operator",
        },
      },
      else: {
        "*": {
          action_: "o=",
        },
      },
    }),
    actions: {
      "tight operator": function tightOperator(buffer, m) {
        buffer.o = (buffer.o || "") + "{" + m + "}";
      },
      output: function output(buffer) {
        if (buffer.o) {
          /** @type {ParserOutput} */
          var ret = {
            type_: "tex-math",
            p1: buffer.o,
          };

          for (var p in buffer) {
            delete buffer[p];
          }

          return ret;
        }
      },
    },
  },
  "9,9": {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": {},
      },
      ",": {
        "*": {
          action_: "comma",
        },
      },
      else: {
        "*": {
          action_: "copy",
        },
      },
    }),
    actions: {
      comma: function comma() {
        return {
          type_: "commaDecimal",
        };
      },
    },
  },
  //#endregion
  //
  // \pu state machines
  //
  //#region pu
  pu: {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": {
          action_: "output",
        },
      },
      space$: {
        "*": {
          action_: ["output", "space"],
        },
      },
      "{[(|)]}": {
        "0|a": {
          action_: "copy",
        },
      },
      "(-)(9)^(-9)": {
        0: {
          action_: "number^",
          nextState: "a",
        },
      },
      "(-)(9.,9)(e)(99)": {
        0: {
          action_: "enumber",
          nextState: "a",
        },
      },
      space: {
        "0|a": {},
      },
      "pm-operator": {
        "0|a": {
          action_: {
            type_: "operator",
            option: "\\pm",
          },
          nextState: "0",
        },
      },
      operator: {
        "0|a": {
          action_: "copy",
          nextState: "0",
        },
      },
      "//": {
        d: {
          action_: "o=",
          nextState: "/",
        },
      },
      "/": {
        d: {
          action_: "o=",
          nextState: "/",
        },
      },
      "{...}|else": {
        "0|d": {
          action_: "d=",
          nextState: "d",
        },
        a: {
          action_: ["space", "d="],
          nextState: "d",
        },
        "/|q": {
          action_: "q=",
          nextState: "q",
        },
      },
    }),
    actions: {
      enumber: function enumber(buffer, m) {
        /** @type {ParserOutput[]} */
        var ret = [];

        if (m[0] === "+-" || m[0] === "+/-") {
          ret.push("\\pm ");
        } else if (m[0]) {
          ret.push(m[0]);
        }

        if (m[1]) {
          mhchemParser.concatArray(ret, mhchemParser.go(m[1], "pu-9,9"));

          if (m[2]) {
            if (m[2].match(/[,.]/)) {
              mhchemParser.concatArray(ret, mhchemParser.go(m[2], "pu-9,9"));
            } else {
              ret.push(m[2]);
            }
          }

          m[3] = m[4] || m[3];

          if (m[3]) {
            m[3] = m[3].trim();

            if (m[3] === "e" || m[3].substr(0, 1) === "*") {
              ret.push({
                type_: "cdot",
              });
            } else {
              ret.push({
                type_: "times",
              });
            }
          }
        }

        if (m[3]) {
          ret.push("10^{" + m[5] + "}");
        }

        return ret;
      },
      "number^": function number(buffer, m) {
        /** @type {ParserOutput[]} */
        var ret = [];

        if (m[0] === "+-" || m[0] === "+/-") {
          ret.push("\\pm ");
        } else if (m[0]) {
          ret.push(m[0]);
        }

        mhchemParser.concatArray(ret, mhchemParser.go(m[1], "pu-9,9"));
        ret.push("^{" + m[2] + "}");
        return ret;
      },
      operator: function operator(buffer, m, p1) {
        return {
          type_: "operator",
          kind_: p1 || m,
        };
      },
      space: function space() {
        return {
          type_: "pu-space-1",
        };
      },
      output: function output(buffer) {
        /** @type {ParserOutput | ParserOutput[]} */
        var ret;
        var md = mhchemParser.patterns.match_("{(...)}", buffer.d || "");

        if (md && md.remainder === "") {
          buffer.d = md.match_;
        }

        var mq = mhchemParser.patterns.match_("{(...)}", buffer.q || "");

        if (mq && mq.remainder === "") {
          buffer.q = mq.match_;
        }

        if (buffer.d) {
          buffer.d = buffer.d.replace(/\u00B0C|\^oC|\^{o}C/g, "{}^{\\circ}C");
          buffer.d = buffer.d.replace(/\u00B0F|\^oF|\^{o}F/g, "{}^{\\circ}F");
        }

        if (buffer.q) {
          // fraction
          buffer.q = buffer.q.replace(/\u00B0C|\^oC|\^{o}C/g, "{}^{\\circ}C");
          buffer.q = buffer.q.replace(/\u00B0F|\^oF|\^{o}F/g, "{}^{\\circ}F");
          var b5 = {
            d: mhchemParser.go(buffer.d, "pu"),
            q: mhchemParser.go(buffer.q, "pu"),
          };

          if (buffer.o === "//") {
            ret = {
              type_: "pu-frac",
              p1: b5.d,
              p2: b5.q,
            };
          } else {
            ret = b5.d;

            if (b5.d.length > 1 || b5.q.length > 1) {
              ret.push({
                type_: " / ",
              });
            } else {
              ret.push({
                type_: "/",
              });
            }

            mhchemParser.concatArray(ret, b5.q);
          }
        } else {
          // no fraction
          ret = mhchemParser.go(buffer.d, "pu-2");
        }

        for (var p in buffer) {
          delete buffer[p];
        }

        return ret;
      },
    },
  },
  "pu-2": {
    transitions: mhchemParser.createTransitions({
      empty: {
        "*": {
          action_: "output",
        },
      },
      "*": {
        "*": {
          action_: ["output", "cdot"],
          nextState: "0",
        },
      },
      "\\x": {
        "*": {
          action_: "rm=",
        },
      },
      space: {
        "*": {
          action_: ["output", "space"],
          nextState: "0",
        },
      },
      "^{(...)}|^(-1)": {
        1: {
          action_: "^(-1)",
        },
      },
      "-9.,9": {
        0: {
          action_: "rm=",
          nextState: "0",
        },
        1: {
          action_: "^(-1)",
          nextState: "0",
        },
      },
      "{...}|else": {
        "*": {
          action_: "rm=",
          nextState: "1",
        },
      },
    }),
    actions: {
      cdot: function cdot() {
        return {
          type_: "tight cdot",
        };
      },
      "^(-1)": function _(buffer, m) {
        buffer.rm += "^{" + m + "}";
      },
      space: function space() {
        return {
          type_: "pu-space-2",
        };
      },
      output: function output(buffer) {
        /** @type {ParserOutput | ParserOutput[]} */
        var ret = [];

        if (buffer.rm) {
          var mrm = mhchemParser.patterns.match_("{(...)}", buffer.rm || "");

          if (mrm && mrm.remainder === "") {
            ret = mhchemParser.go(mrm.match_, "pu");
          } else {
            ret = {
              type_: "rm",
              p1: buffer.rm,
            };
          }
        }

        for (var p in buffer) {
          delete buffer[p];
        }

        return ret;
      },
    },
  },
  "pu-9,9": {
    transitions: mhchemParser.createTransitions({
      empty: {
        0: {
          action_: "output-0",
        },
        o: {
          action_: "output-o",
        },
      },
      ",": {
        0: {
          action_: ["output-0", "comma"],
          nextState: "o",
        },
      },
      ".": {
        0: {
          action_: ["output-0", "copy"],
          nextState: "o",
        },
      },
      else: {
        "*": {
          action_: "text=",
        },
      },
    }),
    actions: {
      comma: function comma() {
        return {
          type_: "commaDecimal",
        };
      },
      "output-0": function output0(buffer) {
        /** @type {ParserOutput[]} */
        var ret = [];
        buffer.text_ = buffer.text_ || "";

        if (buffer.text_.length > 4) {
          var a = buffer.text_.length % 3;

          if (a === 0) {
            a = 3;
          }

          for (var i = buffer.text_.length - 3; i > 0; i -= 3) {
            ret.push(buffer.text_.substr(i, 3));
            ret.push({
              type_: "1000 separator",
            });
          }

          ret.push(buffer.text_.substr(0, a));
          ret.reverse();
        } else {
          ret.push(buffer.text_);
        }

        for (var p in buffer) {
          delete buffer[p];
        }

        return ret;
      },
      "output-o": function outputO(buffer) {
        /** @type {ParserOutput[]} */
        var ret = [];
        buffer.text_ = buffer.text_ || "";

        if (buffer.text_.length > 4) {
          var a = buffer.text_.length - 3;

          for (var i = 0; i < a; i += 3) {
            ret.push(buffer.text_.substr(i, 3));
            ret.push({
              type_: "1000 separator",
            });
          }

          ret.push(buffer.text_.substr(i));
        } else {
          ret.push(buffer.text_);
        }

        for (var p in buffer) {
          delete buffer[p];
        }

        return ret;
      },
    },
  }, //#endregion
}; //
// texify: Take MhchemParser output and convert it to TeX
//

/** @type {Texify} */

var texify = {
  go: function go(input, isInner) {
    // (recursive, max 4 levels)
    if (!input) {
      return "";
    }

    var res = "";
    var cee = false;

    for (var i = 0; i < input.length; i++) {
      var inputi = input[i];

      if (typeof inputi === "string") {
        res += inputi;
      } else {
        res += texify._go2(inputi);

        if (inputi.type_ === "1st-level escape") {
          cee = true;
        }
      }
    }

    if (!isInner && !cee && res) {
      res = "{" + res + "}";
    }

    return res;
  },
  _goInner: function _goInner(input) {
    if (!input) {
      return input;
    }

    return texify.go(input, true);
  },
  _go2: function _go2(buf) {
    /** @type {undefined | string} */
    var res;

    switch (buf.type_) {
      case "chemfive":
        res = "";
        var b5 = {
          a: texify._goInner(buf.a),
          b: texify._goInner(buf.b),
          p: texify._goInner(buf.p),
          o: texify._goInner(buf.o),
          q: texify._goInner(buf.q),
          d: texify._goInner(buf.d),
        }; //
        // a
        //

        if (b5.a) {
          if (b5.a.match(/^[+\-]/)) {
            b5.a = "{" + b5.a + "}";
          }

          res += b5.a + "\\,";
        } //
        // b and p
        //

        if (b5.b || b5.p) {
          res += "{\\vphantom{X}}";
          res +=
            "^{\\hphantom{" +
            (b5.b || "") +
            "}}_{\\hphantom{" +
            (b5.p || "") +
            "}}";
          res += "{\\vphantom{X}}";
          res += "^{\\smash[t]{\\vphantom{2}}\\mathllap{" + (b5.b || "") + "}}";
          res += "_{\\vphantom{2}\\mathllap{\\smash[t]{" + (b5.p || "") + "}}}";
        } //
        // o
        //

        if (b5.o) {
          if (b5.o.match(/^[+\-]/)) {
            b5.o = "{" + b5.o + "}";
          }

          res += b5.o;
        } //
        // q and d
        //

        if (buf.dType === "kv") {
          if (b5.d || b5.q) {
            res += "{\\vphantom{X}}";
          }

          if (b5.d) {
            res += "^{" + b5.d + "}";
          }

          if (b5.q) {
            res += "_{\\smash[t]{" + b5.q + "}}";
          }
        } else if (buf.dType === "oxidation") {
          if (b5.d) {
            res += "{\\vphantom{X}}";
            res += "^{" + b5.d + "}";
          }

          if (b5.q) {
            res += "{\\vphantom{X}}";
            res += "_{\\smash[t]{" + b5.q + "}}";
          }
        } else {
          if (b5.q) {
            res += "{\\vphantom{X}}";
            res += "_{\\smash[t]{" + b5.q + "}}";
          }

          if (b5.d) {
            res += "{\\vphantom{X}}";
            res += "^{" + b5.d + "}";
          }
        }

        break;

      case "rm":
        res = "\\mathrm{" + buf.p1 + "}";
        break;

      case "text":
        if (buf.p1.match(/[\^_]/)) {
          buf.p1 = buf.p1.replace(" ", "~").replace("-", "\\text{-}");
          res = "\\mathrm{" + buf.p1 + "}";
        } else {
          res = "\\text{" + buf.p1 + "}";
        }

        break;

      case "roman numeral":
        res = "\\mathrm{" + buf.p1 + "}";
        break;

      case "state of aggregation":
        res = "\\mskip2mu " + texify._goInner(buf.p1);
        break;

      case "state of aggregation subscript":
        res = "\\mskip1mu " + texify._goInner(buf.p1);
        break;

      case "bond":
        res = texify._getBond(buf.kind_);

        if (!res) {
          throw [
            "MhchemErrorBond",
            "mhchem Error. Unknown bond type (" + buf.kind_ + ")",
          ];
        }

        break;

      case "frac":
        var c = "\\frac{" + buf.p1 + "}{" + buf.p2 + "}";
        res =
          "\\mathchoice{\\textstyle" + c + "}{" + c + "}{" + c + "}{" + c + "}";
        break;

      case "pu-frac":
        var d =
          "\\frac{" +
          texify._goInner(buf.p1) +
          "}{" +
          texify._goInner(buf.p2) +
          "}";
        res =
          "\\mathchoice{\\textstyle" + d + "}{" + d + "}{" + d + "}{" + d + "}";
        break;

      case "tex-math":
        res = buf.p1 + " ";
        break;

      case "frac-ce":
        res =
          "\\frac{" +
          texify._goInner(buf.p1) +
          "}{" +
          texify._goInner(buf.p2) +
          "}";
        break;

      case "overset":
        res =
          "\\overset{" +
          texify._goInner(buf.p1) +
          "}{" +
          texify._goInner(buf.p2) +
          "}";
        break;

      case "underset":
        res =
          "\\underset{" +
          texify._goInner(buf.p1) +
          "}{" +
          texify._goInner(buf.p2) +
          "}";
        break;

      case "underbrace":
        res =
          "\\underbrace{" +
          texify._goInner(buf.p1) +
          "}_{" +
          texify._goInner(buf.p2) +
          "}";
        break;

      case "color":
        res =
          "{\\color{" + buf.color1 + "}{" + texify._goInner(buf.color2) + "}}";
        break;

      case "color0":
        res = "\\color{" + buf.color + "}";
        break;

      case "arrow":
        var b6 = {
          rd: texify._goInner(buf.rd),
          rq: texify._goInner(buf.rq),
        };

        var arrow = "\\x" + texify._getArrow(buf.r);

        if (b6.rq) {
          arrow += "[{" + b6.rq + "}]";
        }

        if (b6.rd) {
          arrow += "{" + b6.rd + "}";
        } else {
          arrow += "{}";
        }

        res = arrow;
        break;

      case "operator":
        res = texify._getOperator(buf.kind_);
        break;

      case "1st-level escape":
        res = buf.p1 + " "; // &, \\\\, \\hlin

        break;

      case "space":
        res = " ";
        break;

      case "entitySkip":
        res = "~";
        break;

      case "pu-space-1":
        res = "~";
        break;

      case "pu-space-2":
        res = "\\mkern3mu ";
        break;

      case "1000 separator":
        res = "\\mkern2mu ";
        break;

      case "commaDecimal":
        res = "{,}";
        break;

      case "comma enumeration L":
        res = "{" + buf.p1 + "}\\mkern6mu ";
        break;

      case "comma enumeration M":
        res = "{" + buf.p1 + "}\\mkern3mu ";
        break;

      case "comma enumeration S":
        res = "{" + buf.p1 + "}\\mkern1mu ";
        break;

      case "hyphen":
        res = "\\text{-}";
        break;

      case "addition compound":
        res = "\\,{\\cdot}\\,";
        break;

      case "electron dot":
        res = "\\mkern1mu \\bullet\\mkern1mu ";
        break;

      case "KV x":
        res = "{\\times}";
        break;

      case "prime":
        res = "\\prime ";
        break;

      case "cdot":
        res = "\\cdot ";
        break;

      case "tight cdot":
        res = "\\mkern1mu{\\cdot}\\mkern1mu ";
        break;

      case "times":
        res = "\\times ";
        break;

      case "circa":
        res = "{\\sim}";
        break;

      case "^":
        res = "uparrow";
        break;

      case "v":
        res = "downarrow";
        break;

      case "ellipsis":
        res = "\\ldots ";
        break;

      case "/":
        res = "/";
        break;

      case " / ":
        res = "\\,/\\,";
        break;

      default:
        throw ["MhchemBugT", "mhchem bug T. Please report."];
      // Missing texify rule or unknown MhchemParser output
    }
    return res;
  },
  _getArrow: function _getArrow(a) {
    switch (a) {
      case "->":
        return "rightarrow";

      case "\u2192":
        return "rightarrow";

      case "\u27F6":
        return "rightarrow";

      case "<-":
        return "leftarrow";

      case "<->":
        return "leftrightarrow";

      case "<-->":
        return "rightleftarrows";

      case "<=>":
        return "rightleftharpoons";

      case "\u21CC":
        return "rightleftharpoons";

      case "<=>>":
        return "rightequilibrium";

      case "<<=>":
        return "leftequilibrium";

      default:
        throw ["MhchemBugT", "mhchem bug T. Please report."];
    }
  },
  _getBond: function _getBond(a) {
    switch (a) {
      case "-":
        return "{-}";

      case "1":
        return "{-}";

      case "=":
        return "{=}";

      case "2":
        return "{=}";

      case "#":
        return "{\\equiv}";

      case "3":
        return "{\\equiv}";

      case "~":
        return "{\\tripledash}";

      case "~-":
        return "{\\mathrlap{\\raisebox{-.1em}{$-$}}\\raisebox{.1em}{$\\tripledash$}}";

      case "~=":
        return "{\\mathrlap{\\raisebox{-.2em}{$-$}}\\mathrlap{\\raisebox{.2em}{$\\tripledash$}}-}";

      case "~--":
        return "{\\mathrlap{\\raisebox{-.2em}{$-$}}\\mathrlap{\\raisebox{.2em}{$\\tripledash$}}-}";

      case "-~-":
        return "{\\mathrlap{\\raisebox{-.2em}{$-$}}\\mathrlap{\\raisebox{.2em}{$-$}}\\tripledash}";

      case "...":
        return "{{\\cdot}{\\cdot}{\\cdot}}";

      case "....":
        return "{{\\cdot}{\\cdot}{\\cdot}{\\cdot}}";

      case "->":
        return "{\\rightarrow}";

      case "<-":
        return "{\\leftarrow}";

      case "<":
        return "{<}";

      case ">":
        return "{>}";

      default:
        throw ["MhchemBugT", "mhchem bug T. Please report."];
    }
  },
  _getOperator: function _getOperator(a) {
    switch (a) {
      case "+":
        return " {}+{} ";

      case "-":
        return " {}-{} ";

      case "=":
        return " {}={} ";

      case "<":
        return " {}<{} ";

      case ">":
        return " {}>{} ";

      case "<<":
        return " {}\\ll{} ";

      case ">>":
        return " {}\\gg{} ";

      case "\\pm":
        return " {}\\pm{} ";

      case "\\approx":
        return " {}\\approx{} ";

      case "$\\approx$":
        return " {}\\approx{} ";

      case "v":
        return " \\downarrow{} ";

      case "(v)":
        return " \\downarrow{} ";

      case "^":
        return " \\uparrow{} ";

      case "(^)":
        return " \\uparrow{} ";

      default:
        throw ["MhchemBugT", "mhchem bug T. Please report."];
    }
  },
}; //
